﻿using System;
using System.Collections.ObjectModel;
using System.Web;
using OperationPlugins.Handlers;
using OperationPlugins.Validators;
using OperationPlugins.Validators.DataAnnotations;

namespace OperationPlugins
{
    public sealed class OperationPluginsContext
    {
        private const string ContextKey = "OperationPluginsContext";

        private readonly ValidationResultMessageContainer _results = new ValidationResultMessageContainer();

        /// <summary>
        /// No public constructor available, please use the <see cref="Current"/> property.
        /// </summary>
        private OperationPluginsContext()
        {
        }

        /// <summary>
        /// Gets the current OperationPluginsContext object.
        /// </summary>
        public static OperationPluginsContext Current
        {
            get
            {
                HttpContext http = HttpContext.Current;

                if (http == null)
                {
                    throw new OperationPluginsContextException("The HttpContext instance for the current HTTP request is not available.");
                }

                OperationPluginsContext current = http.Items[ContextKey] as OperationPluginsContext;

                if (current == null)
                {
                    http.Items[ContextKey] = current = new OperationPluginsContext();                    
                }

                return current;
            }
        }

        /// <summary>
        /// Adds a validation result to this OperationPluginsContext object.
        /// </summary>
        /// <param name="result">The validation result to be added.</param>
        private void Add(ValidationResultMessageContainer result)
        {
            _results.Add(result);
        }

        /// <summary>
        /// Gets a Boolean value indicating whether this OperationPluginsContext object 
        /// contains any error messages.
        /// </summary>
        public bool ContainsErrors
        {
            get { return _results.ContainsErrors; }
        }

        /// <summary>
        /// Gets a Boolean value indicating whether this OperationPluginsContext object 
        /// contains any notification messages.
        /// </summary>
        public bool ContainsNotifications
        {
            get { return _results.ContainsNotifications; }
        }

        /// <summary>
        /// Gets all error messages.
        /// </summary>
        public ReadOnlyCollection<ValidationResultMessage> Errors
        {
            get { return _results.Errors; }
        }

        /// <summary>
        /// Gets all notification messages.
        /// </summary>
        public ReadOnlyCollection<ValidationResultMessage> Notifications
        {
            get { return _results.Notifications; }
        }

        /// <summary>
        /// Gets and sets the OperationId for the current action.
        /// </summary>
        public OperationId CurrentActionOperation { get; set; }

        /// <summary>
        /// Gets and sets the BrachId for the current action.
        /// </summary>
        public BranchId CurrentActionBranch { get; set; }

        /// <summary>
        /// Gets and sets the value indicating should the current action ignore all <see cref="UsePluginValidatorsAttribute"/> attributes.
        /// </summary>
        public bool DisablePluginsValidation { get; set; }

        /// <summary>
        /// Clears validation results from this context. 
        /// </summary>
        public void ClearResults()
        {
            _results.Clear();
        }


        [HandlerOf(typeof(InternalEnvelope<ValidationResultMessageContainer>))]
        private sealed class ValidationResultMessageContainerHandler : InternalEnvelope<ValidationResultMessageContainer>.Handler
        {
            protected override void Handle()
            {
                Current.Add(Value);
            }
        }

        /// <summary>
        /// The exception that is thrown when a fatal error occurs inside OperationPluginsContext.
        /// </summary>
        public sealed class OperationPluginsContextException : Exception
        {
            public OperationPluginsContextException(string message)
                : base(message)
            {
            }
        }

    }
}
